home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / pyshared / ufw / frontend.py < prev    next >
Encoding:
Python Source  |  2008-10-08  |  28.1 KB  |  817 lines

  1. #
  2. # frontend.py: frontend interface for ufw
  3. #
  4. # Copyright (C) 2008 Canonical Ltd.
  5. #
  6. #    This program is free software: you can redistribute it and/or modify
  7. #    it under the terms of the GNU General Public License version 3,
  8. #    as published by the Free Software Foundation.
  9. #
  10. #    This program is distributed in the hope that it will be useful,
  11. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #    GNU General Public License for more details.
  14. #
  15. #    You should have received a copy of the GNU General Public License
  16. #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17. #
  18.  
  19. import re
  20. import os
  21. import sys
  22. import warnings
  23.  
  24. from ufw.common import UFWError
  25. import ufw.util
  26. from ufw.util import error, warn
  27. from ufw.backend_iptables import UFWBackendIptables
  28.  
  29. def parse_command(argv):
  30.     '''Parse command. Returns tuple for action, rule, ip_version and dryrun.'''
  31.     action = ""
  32.     rule = ""
  33.     type = ""
  34.     from_type = "any"
  35.     to_type = "any"
  36.     from_service = ""
  37.     to_service = ""
  38.     dryrun = False
  39.  
  40.     if len(argv) > 1 and argv[1].lower() == "--dry-run":
  41.         dryrun = True
  42.         argv.remove(argv[1])
  43.  
  44.     remove = False
  45.     if len(argv) > 1 and argv[1].lower() == "delete":
  46.         remove = True
  47.         argv.remove(argv[1])
  48.  
  49.     nargs = len(argv)
  50.  
  51.     if nargs < 2:
  52.         raise ValueError()
  53.  
  54.     allowed_cmds = ['enable', 'disable', 'help', '--help', 'default', \
  55.                     'logging', 'status', 'version', '--version', 'allow', \
  56.                     'deny', 'limit', 'reload' ]
  57.  
  58.     if not argv[1].lower() in allowed_cmds:
  59.         raise ValueError()
  60.     else:
  61.         action = argv[1].lower()
  62.  
  63.     if action == "logging":
  64.         if nargs < 3:
  65.             raise ValueError()
  66.         elif argv[2].lower() == "off":
  67.             action = "logging-off"
  68.         elif argv[2].lower() == "on":
  69.             action = "logging-on"
  70.         else:
  71.             raise ValueError()
  72.  
  73.     if action == "status":
  74.         if nargs > 2 and argv[2].lower() == "verbose":
  75.             action = "status-verbose"
  76.  
  77.     if action == "default":
  78.         if nargs < 3:
  79.             raise ValueError()
  80.         elif argv[2].lower() == "deny":
  81.             action = "default-deny"
  82.         elif argv[2].lower() == "allow":
  83.             action = "default-allow"
  84.         else:
  85.             raise ValueError()
  86.  
  87.     if action == "allow" or action == "deny" or action == "limit":
  88.         if nargs < 3 or nargs > 12:
  89.             raise ValueError()
  90.  
  91.         rule = ufw.common.UFWRule(action, "any", "any")
  92.         if remove:
  93.             rule.remove = remove
  94.         if nargs == 3:
  95.             # Short form where only app or port/proto is given
  96.             if ufw.applications.valid_profile_name(argv[2]):
  97.                 # Check if name collision with /etc/services. If so, use
  98.                 # /etc/services instead of application profile
  99.                 try:
  100.                     ufw.util.get_services_proto(argv[2])
  101.                 except Exception:
  102.                     type = "both"
  103.                     rule.dapp = argv[2]
  104.                     rule.set_port(argv[2], "dst")
  105.             if rule.dapp == "":
  106.                 try:
  107.                     (port, proto) = ufw.util.parse_port_proto(argv[2])
  108.                 except UFWError:
  109.                     err_msg = _("Bad port")
  110.                     raise UFWError(err_msg)
  111.  
  112.                 if not re.match('^\d([0-9,:]*\d+)*$', port):
  113.                     if ',' in port or ':' in port:
  114.                         err_msg = _("Port ranges must be numeric")
  115.                         raise UFWError(err_msg)
  116.                     to_service = port
  117.  
  118.                 try:
  119.                     rule.set_protocol(proto)
  120.                     rule.set_port(port, "dst")
  121.                     type = "both"
  122.                 except UFWError:
  123.                     err_msg = _("Bad port")
  124.                     raise UFWError(err_msg)
  125.         elif nargs % 2 != 0:
  126.             err_msg = _("Wrong number of arguments")
  127.             raise UFWError(err_msg)
  128.         elif not 'from' in argv and not 'to' in argv:
  129.             err_msg = _("Need 'to' or 'from' clause")
  130.             raise UFWError(err_msg)
  131.         else:
  132.             # Full form with PF-style syntax
  133.             keys = [ 'proto', 'from', 'to', 'port', 'app' ]
  134.  
  135.             # quick check
  136.             if argv.count("to") > 1 or \
  137.                argv.count("from") > 1 or \
  138.                argv.count("proto") > 1 or \
  139.                argv.count("port") > 2 or \
  140.                argv.count("app") > 2 or \
  141.                argv.count("app") > 0 and argv.count("proto") > 0:
  142.                 err_msg = _("Improper rule syntax")
  143.                 raise UFWError(err_msg)
  144.  
  145.             i = 1
  146.             loc = ""
  147.             for arg in argv[1:]:
  148.                 if i % 2 == 0 and argv[i] not in keys:
  149.                     err_msg = _("Invalid token '%s'") % (argv[i])
  150.                     raise UFWError(err_msg)
  151.                 if arg == "proto":
  152.                     if i+1 < nargs:
  153.                         try:
  154.                             rule.set_protocol(argv[i+1])
  155.                         except Exception:
  156.                             raise
  157.                     else:
  158.                         err_msg = _("Invalid 'proto' clause")
  159.                         raise UFWError(err_msg)
  160.                 elif arg == "from":
  161.                     if i+1 < nargs:
  162.                         try:
  163.                             faddr = argv[i+1].lower()
  164.                             if faddr == "any":
  165.                                 faddr = "0.0.0.0/0"
  166.                                 from_type = "any"
  167.                             else:
  168.                                 if ufw.util.valid_address(faddr, "6"):
  169.                                     from_type = "v6"
  170.                                 else:
  171.                                     from_type = "v4"
  172.                             rule.set_src(faddr)
  173.                         except Exception:
  174.                             raise
  175.                         loc = "src"
  176.                     else:
  177.                         err_msg = _("Invalid 'from' clause")
  178.                         raise UFWError(err_msg)
  179.                 elif arg == "to":
  180.                     if i+1 < nargs:
  181.                         try:
  182.                             saddr = argv[i+1].lower()
  183.                             if saddr == "any":
  184.                                 saddr = "0.0.0.0/0"
  185.                                 to_type = "any"
  186.                             else:
  187.                                 if ufw.util.valid_address(saddr, "6"):
  188.                                     to_type = "v6"
  189.                                 else:
  190.                                     to_type = "v4"
  191.                             rule.set_dst(saddr)
  192.                         except Exception:
  193.                             raise
  194.                         loc = "dst"
  195.                     else:
  196.                         err_msg = _("Invalid 'to' clause")
  197.                         raise UFWError(err_msg)
  198.                 elif arg == "port" or arg == "app":
  199.                     if i+1 < nargs:
  200.                         if loc == "":
  201.                             err_msg = _("Need 'from' or 'to' with '%s'") % \
  202.                                         (arg)
  203.                             raise UFWError(err_msg)
  204.  
  205.                         tmp = argv[i+1]
  206.                         if arg == "app":
  207.                             if loc == "src":
  208.                                 rule.sapp = tmp
  209.                             else:
  210.                                 rule.dapp = tmp
  211.                         elif not re.match('^\d([0-9,:]*\d+)*$', tmp):
  212.                             if ',' in tmp or ':' in tmp:
  213.                                 err_msg = _("Port ranges must be numeric")
  214.                                 raise UFWError(err_msg)
  215.  
  216.                             if loc == "src":
  217.                                 from_service = tmp
  218.                             else:
  219.                                 to_service = tmp
  220.                         try:
  221.                             rule.set_port(tmp, loc)
  222.                         except Exception:
  223.                             raise
  224.                     else:
  225.                         err_msg = _("Invalid 'port' clause")
  226.                         raise UFWError(err_msg)
  227.                 i += 1
  228.  
  229.             # Figure out the type of rule (IPv4, IPv6, or both) this is
  230.             if from_type == "any" and to_type == "any":
  231.                 type = "both"
  232.             elif from_type != "any" and to_type != "any" and \
  233.                  from_type != to_type:
  234.                 err_msg = _("Mixed IP versions for 'from' and 'to'")
  235.                 raise UFWError(err_msg)
  236.             elif from_type != "any":
  237.                 type = from_type
  238.             elif to_type != "any":
  239.                 type = to_type
  240.  
  241.     # Adjust protocol
  242.     if to_service != "" or from_service != "":
  243.         proto = ""
  244.         if to_service != "":
  245.             try:
  246.                 proto = ufw.util.get_services_proto(to_service)
  247.             except Exception:
  248.                 err_msg = _("Could not find protocol")
  249.                 raise UFWError(err_msg)
  250.         if from_service != "":
  251.             if proto == "any" or proto == "":
  252.                 try:
  253.                     proto = ufw.util.get_services_proto(from_service)
  254.                 except Exception:
  255.                     err_msg = _("Could not find protocol")
  256.                     raise UFWError(err_msg)
  257.             else:
  258.                 try:
  259.                     tmp = ufw.util.get_services_proto(from_service)
  260.                 except Exception:
  261.                     err_msg = _("Could not find protocol")
  262.                     raise UFWError(err_msg)
  263.                 if proto == "any" or proto == tmp:
  264.                     proto = tmp
  265.                 elif tmp == "any":
  266.                     pass
  267.                 else:
  268.                     err_msg = _("Protocol mismatch (from/to)")
  269.                     raise UFWError(err_msg)
  270.  
  271.         # Verify found proto with specified proto
  272.         if rule.protocol == "any":
  273.             rule.set_protocol(proto)
  274.         elif proto != "any" and rule.protocol != proto:
  275.             err_msg = _("Protocol mismatch with specified protocol %s") % \
  276.                         (rule.protocol)
  277.             raise UFWError(err_msg)
  278.  
  279.     # Verify protocol not specified with application rule
  280.     if rule and rule.protocol != "any" and \
  281.        (rule.sapp != "" or rule.dapp != ""):
  282.         app = ""
  283.         if rule.dapp:
  284.             app = rule.dapp
  285.         else:
  286.             app = rule.sapp
  287.         err_msg = _("Improper rule syntax ('%s' specified with app rule)") % \
  288.                    (rule.protocol)
  289.         raise UFWError(err_msg)
  290.  
  291.     return (action, rule, type, dryrun)
  292.  
  293.  
  294. def parse_application_command(argv):
  295.     '''Parse applications command. Returns tuple for action and profile name'''
  296.     name = ""
  297.     action = ""
  298.     dryrun = False
  299.     addnew = False
  300.  
  301.     if len(argv) < 3 or argv[1].lower() != "app":
  302.         raise ValueError()
  303.  
  304.     argv.remove("app")
  305.     nargs = len(argv)
  306.  
  307.     if len(argv) > 1 and argv[1].lower() == "--dry-run":
  308.         dryrun = True
  309.         argv.remove(argv[1])
  310.  
  311.     app_cmds = ['list', 'info', 'default', 'update']
  312.  
  313.     if not argv[1].lower() in app_cmds:
  314.         raise ValueError()
  315.     else:
  316.         action = argv[1].lower()
  317.  
  318.     if action == "info" or action == "update":
  319.         if nargs >= 4 and argv[2] == "--add-new":
  320.             addnew = True
  321.             argv.remove("--add-new")
  322.             nargs = len(argv)
  323.  
  324.         if nargs < 3:
  325.             raise ValueError()
  326.  
  327.         # Handle quoted name with spaces in it by stripping Python's ['...']
  328.         # list as string text.
  329.         name = str(argv[2]).strip("[']")
  330.  
  331.         if addnew:
  332.             action += "-with-new"
  333.  
  334.     if action == "list" and nargs != 2:
  335.         raise ValueError()
  336.  
  337.     if action == "default":
  338.         if nargs < 3:
  339.             raise ValueError()
  340.         if argv[2].lower() == "allow":
  341.             action = "default-allow"
  342.         elif argv[2].lower() == "deny":
  343.             action = "default-deny"
  344.         elif argv[2].lower() == "skip":
  345.             action = "default-skip"
  346.         else:
  347.             raise ValueError()
  348.  
  349.     return (action, name, dryrun)
  350.  
  351.  
  352. def get_command_help():
  353.     '''Print help message'''
  354.     msg = _('''
  355. Usage: ''') + ufw.common.programName + _(''' COMMAND
  356.  
  357. Commands:
  358.   enable            enables the firewall
  359.   disable            disables the firewall
  360.   default ARG            set default policy to ALLOW or DENY
  361.   logging ARG            set logging to ON or OFF
  362.   allow|deny RULE        allow or deny RULE
  363.   delete allow|deny RULE    delete the allow/deny RULE
  364.   status            show firewall status
  365.   version            display version information
  366.  
  367. Application profile commands:
  368.   app list            list application profiles
  369.   app info PROFILE        show information on PROFILE
  370.   app update PROFILE        update PROFILE
  371.   app default ARG        set profile policy to ALLOW, DENY or SKIP
  372. ''')
  373.     return (msg)
  374.  
  375.  
  376. class UFWFrontend:
  377.     '''UI'''
  378.     def __init__(self, dryrun, backend_type="iptables"):
  379.         if backend_type == "iptables":
  380.             try:
  381.                 self.backend = UFWBackendIptables(dryrun)
  382.             except Exception:
  383.                 raise
  384.         else:
  385.             raise UFWError("Unsupported backend type '%s'" % (backend_type))
  386.  
  387.     def set_enabled(self, enabled):
  388.         '''Toggles ENABLED state in of <config_dir>/ufw/ufw.conf'''
  389.         res = ""
  390.  
  391.         str = "no"
  392.         if enabled:
  393.             str = "yes"
  394.  
  395.         changed = False
  396.         if (enabled and not self.backend._is_enabled()) or \
  397.            (not enabled and self.backend._is_enabled()):
  398.             changed = True
  399.  
  400.         # Update the config files when toggling enable/disable
  401.         if changed:
  402.             try:
  403.                 self.backend.set_default(self.backend.files['conf'], \
  404.                                          "ENABLED", str)
  405.             except UFWError, e:
  406.                 error(e.value)
  407.  
  408.         error_str = ""
  409.         if enabled:
  410.             try:
  411.                 self.backend.start_firewall()
  412.             except UFWError, e:
  413.                 if changed:
  414.                     error_str = e.value
  415.  
  416.             if error_str != "":
  417.                 # Revert config files when toggling enable/disable and
  418.                 # firewall failed to start
  419.                 try:
  420.                     self.backend.set_default(self.backend.files['conf'], \
  421.                                              "ENABLED", "no")
  422.                 except UFWError, e:
  423.                     error(e.value)
  424.  
  425.                 # Report the error
  426.                 error(error_str)
  427.  
  428.             res = _("Firewall started and enabled on system startup")
  429.         else:
  430.             try:
  431.                 self.backend.stop_firewall()
  432.             except UFWError, e:
  433.                 error(e.value)
  434.  
  435.             res = _("Firewall stopped and disabled on system startup")
  436.  
  437.         return res
  438.  
  439.     def set_default_policy(self, policy):
  440.         '''Sets default policy of firewall'''
  441.         res = ""
  442.         try:
  443.             res = self.backend.set_default_policy(policy)
  444.             if self.backend._is_enabled():
  445.                 self.backend.stop_firewall()
  446.                 self.backend.start_firewall()
  447.         except UFWError, e:
  448.             error(e.value)
  449.  
  450.         return res
  451.  
  452.     def set_loglevel(self, level):
  453.         '''Sets log level of firewall'''
  454.         res = ""
  455.         try:
  456.             res = self.backend.set_loglevel(level)
  457.             if self.backend._is_enabled():
  458.                 # have to just restart because of ordering of LOG rules
  459.                 self.backend.stop_firewall()
  460.                 self.backend.start_firewall()
  461.         except UFWError, e:
  462.             error(e.value)
  463.  
  464.         return res
  465.  
  466.     def get_status(self, verbose=False):
  467.         '''Shows status of firewall'''
  468.         try:
  469.             out = self.backend.get_status(verbose)
  470.         except UFWError, e:
  471.             error(e.value)
  472.  
  473.         return out
  474.  
  475.     def set_rule(self, rule, ip_version):
  476.         '''Updates firewall with rule'''
  477.         res = ""
  478.         err_msg = ""
  479.         tmp = ""
  480.         rules = []
  481.  
  482.         if rule.dapp == "" and rule.sapp == "":
  483.             rules.append(rule)
  484.         else:
  485.             tmprules = []
  486.             try:
  487.                 if rule.remove:
  488.                     if ip_version == "v4":
  489.                         tmprules = self.backend.get_app_rules_from_system(rule, False)
  490.                     elif ip_version == "v6":
  491.                         tmprules = self.backend.get_app_rules_from_system(rule, True)
  492.                     elif ip_version == "both":
  493.                         tmprules = self.backend.get_app_rules_from_system(rule, False)
  494.                         tmprules6 = self.backend.get_app_rules_from_system(rule, True)
  495.                         # Only add rules that are different by more than v6 (we
  496.                         # will handle 'ip_version == both' specially, below).
  497.                         for x in tmprules:
  498.                             for y in tmprules6:
  499.                                 prev6 = y.v6
  500.                                 y.v6 = False
  501.                                 if not x.match(y):
  502.                                     y.v6 = prev6
  503.                                     tmprules.append(y)
  504.                     else:
  505.                         err_msg = _("Invalid IP version '%s'") % (ip_version)
  506.                         raise UFWError(err_msg)
  507.  
  508.                     # Don't process removal of non-existing application rules
  509.                     if len(tmprules) == 0 and not self.backend.dryrun:
  510.                         tmp =  _("Could not delete non-existent rule")
  511.                         if ip_version == "v4":
  512.                             res = tmp
  513.                         elif ip_version == "v6":
  514.                             res = tmp + " (v6)"
  515.                         elif ip_version == "both":
  516.                             res = tmp + "\n" + tmp + " (v6)"
  517.                         return res
  518.  
  519.                     for tmp in tmprules:
  520.                         r = tmp.dup_rule()
  521.                         r.remove = rule.remove
  522.                         r.set_action(rule.action)
  523.                         rules.append(r)
  524.                 else:
  525.                     rules = self.backend.get_app_rules_from_template(rule)
  526.             except Exception:
  527.                 raise
  528.  
  529.         count = 0
  530.         set_error = False
  531.         for i, r in enumerate(rules):
  532.             count = i
  533.             try:
  534.                 if self.backend.use_ipv6():
  535.                     if ip_version == "v4":
  536.                         r.set_v6(False)
  537.                         tmp = self.backend.set_rule(r)
  538.                     elif ip_version == "v6":
  539.                         r.set_v6(True)
  540.                         tmp = self.backend.set_rule(r)
  541.                     elif ip_version == "both":
  542.                         r.set_v6(False)
  543.                         tmp = self.backend.set_rule(r)
  544.                         r.set_v6(True)
  545.                         if tmp != "":
  546.                             tmp += "\n"
  547.                         tmp += self.backend.set_rule(r)
  548.                     else:
  549.                         err_msg = _("Invalid IP version '%s'") % (ip_version)
  550.                         raise UFWError(err_msg)
  551.                 else:
  552.                     if ip_version == "v4" or ip_version == "both":
  553.                         r.set_v6(False)
  554.                         tmp = self.backend.set_rule(r)
  555.                     elif ip_version == "v6":
  556.                         err_msg = _("IPv6 support not enabled")
  557.                         raise UFWError(err_msg)
  558.                     else:
  559.                         err_msg = _("Invalid IP version '%s'") % (ip_version)
  560.                         raise UFWError(err_msg)
  561.             except UFWError, e:
  562.                 err_msg = e.value
  563.                 set_error = True
  564.                 break
  565.  
  566.             if r.updated:
  567.                 warn_msg = _("Rule changed after normalization")
  568.                 warnings.warn(warn_msg)
  569.  
  570.         if not set_error:
  571.             # Just return the last result if no error
  572.             res += tmp
  573.         elif len(rules) == 1:
  574.             # If no error, and just one rule, error out
  575.             error(err_msg)
  576.         else:
  577.         # If error and more than one rule, delete the successfully added
  578.         # rules in reverse order
  579.             undo_error = False
  580.             indexes = range(count+1)
  581.             indexes.reverse()
  582.             for j in indexes:
  583.                 if count > 0 and rules[j]:
  584.                     backout_rule = rules[j].dup_rule()
  585.                     backout_rule.remove = True
  586.                     try:
  587.                         self.set_rule(backout_rule, ip_version)
  588.                     except Exception:
  589.                         # Don't fail, so we can try to backout more
  590.                         undo_error = True
  591.                         warn_msg = _("Could not back out rule '%s'") % \
  592.                                      r.format_rule()
  593.                         warn(warn_msg)
  594.  
  595.             err_msg += _("\nError applying application rules.")
  596.             if undo_error:
  597.                 err_msg += _(" Some rules could not be unapplied.")
  598.             else:
  599.                 err_msg += _(" Attempted rules successfully unapplied.")
  600.  
  601.             raise UFWError(err_msg)
  602.  
  603.         return res
  604.  
  605.     def do_action(self, action, rule, ip_version):
  606.         '''Perform action on rule. action, rule and ip_version are usually
  607.            based on return values from parse_command().
  608.         '''
  609.         res = ""
  610.         if action == "logging-on":
  611.             res = self.set_loglevel("on")
  612.         elif action == "logging-off":
  613.             res = self.set_loglevel("off")
  614.         elif action == "default-allow":
  615.             res = self.set_default_policy("allow")
  616.         elif action == "default-deny":
  617.             res = self.set_default_policy("deny")
  618.         elif action == "status":
  619.             res = self.get_status()
  620.         elif action == "status-verbose":
  621.             res = self.get_status(True)
  622.         elif action == "enable":
  623.             res = self.set_enabled(True)
  624.         elif action == "disable":
  625.             res = self.set_enabled(False)
  626.         elif action == "reload":
  627.             if self.backend._is_enabled():
  628.                 self.set_enabled(False)
  629.                 self.set_enabled(True)
  630.                 res = _("Firewall reloaded")
  631.             else:
  632.                 res = _("Firewall not enabled (skipping reload)")
  633.         elif action == "allow" or action == "deny" or action == "limit":
  634.             res = self.set_rule(rule, ip_version)
  635.         else:
  636.             err_msg = _("Unsupported action '%s'") % (action)
  637.             raise UFWError(err_msg)
  638.  
  639.         return res
  640.  
  641.     def set_default_application_policy(self, policy):
  642.         '''Sets default application policy of firewall'''
  643.         res = ""
  644.         try:
  645.             res = self.backend.set_default_application_policy(policy)
  646.         except UFWError, e:
  647.             error(e.value)
  648.  
  649.         return res
  650.  
  651.     def get_application_list(self):
  652.         '''Display list of known application profiles'''
  653.         names = self.backend.profiles.keys()
  654.         names.sort()
  655.         rstr = _("Available applications:")
  656.         for n in names:
  657.             rstr += "\n  %s" % (n)
  658.         return rstr
  659.  
  660.     def get_application_info(self, pname):
  661.         '''Display information on profile'''
  662.         names = []
  663.         if pname == "all":
  664.             names = self.backend.profiles.keys()
  665.             names.sort()
  666.         else:
  667.             if not ufw.applications.valid_profile_name(pname):
  668.                 err_msg = _("Invalid profile name")
  669.                 raise UFWError(err_msg)
  670.             names.append(pname)
  671.  
  672.         rstr = ""
  673.         for name in names:
  674.             if not self.backend.profiles.has_key(name) or \
  675.                not self.backend.profiles[name]:
  676.                 err_msg = _("Could not find profile '%s'") % (name)
  677.                 raise UFWError(err_msg)
  678.  
  679.             if not ufw.applications.verify_profile(name, \
  680.                self.backend.profiles[name]):
  681.                 err_msg = _("Invalid profile")
  682.                 raise UFWError(err_msg)
  683.  
  684.             rstr += _("Profile: %s\n") % (name)
  685.             rstr += _("Title: %s\n") % (ufw.applications.get_title(\
  686.                                         self.backend.profiles[name]))
  687.  
  688.             rstr += _("Description: %s\n\n") % \
  689.                                             (ufw.applications.get_description(\
  690.                                              self.backend.profiles[name]))
  691.  
  692.             ports = ufw.applications.get_ports(self.backend.profiles[name])
  693.             if len(ports) > 1 or ',' in ports[0]:
  694.                 rstr += _("Ports:")
  695.             else:
  696.                 rstr += _("Port:")
  697.  
  698.             for p in ports:
  699.                 rstr += "\n  %s" % (p)
  700.  
  701.             if name != names[len(names)-1]:
  702.                 rstr += "\n\n--\n\n"
  703.  
  704.         return ufw.util.wrap_text(rstr)
  705.  
  706.     def application_update(self, profile):
  707.         '''Refresh application profile'''
  708.         rstr = ""
  709.         allow_reload = True
  710.         trigger_reload = False
  711.  
  712.         if self.backend.do_checks and ufw.util.under_ssh():
  713.             # Don't reload the firewall if running under ssh
  714.             allow_reload = False
  715.  
  716.         if profile == "all":
  717.             profiles = self.backend.profiles.keys()
  718.             profiles.sort()
  719.             for p in profiles:
  720.                 (tmp, found) = self.backend.update_app_rule(p)
  721.                 if found:
  722.                     if tmp != "":
  723.                         rstr += "\n"
  724.                     rstr += tmp
  725.                     trigger_reload = found
  726.         else:
  727.             (rstr, trigger_reload) = self.backend.update_app_rule(profile)
  728.             if rstr != "":
  729.                 rstr += "\n"
  730.  
  731.         if trigger_reload and self.backend._is_enabled():
  732.             if allow_reload:
  733.                 try:
  734.                     self.backend._reload_user_rules()
  735.                 except Exception:
  736.                     raise
  737.                 rstr += _("Firewall reloaded")
  738.             else:
  739.                 rstr += _("Skipped reloading firewall")
  740.  
  741.         return rstr
  742.  
  743.     def application_add(self, profile):
  744.         '''Refresh application profile'''
  745.         rstr = ""
  746.         policy = ""
  747.  
  748.         if profile == "all":
  749.             err_msg = _("Cannot specify 'all' with '--add-new'")
  750.             raise UFWError(err_msg)
  751.  
  752.         default = self.backend.defaults['default_application_policy']
  753.         if default == "skip":
  754.             ufw.util.debug("Policy is '%s', not adding profile '%s'" % \
  755.                            (policy, profile))
  756.             return rstr
  757.         elif default == "accept":
  758.             policy = "allow"
  759.         elif default == "drop":
  760.             policy = "deny"
  761.         else:
  762.             err_msg = _("Unknown policy '%s'") % (default)
  763.             raise UFWError(err_msg)
  764.  
  765.         args = [ 'ufw' ]
  766.         if self.backend.dryrun:
  767.             args.append("--dry-run")
  768.  
  769.         args += [ policy, profile ]
  770.         try:
  771.             (action, rule, ip_version, self.backend.dryrun) = \
  772.                 parse_command(args)
  773.         except Exception:
  774.             raise
  775.  
  776.         rstr = self.do_action(action, rule, ip_version)
  777.         return rstr
  778.  
  779.     def do_application_action(self, action, profile):
  780.         '''Perform action on profile. action and profile are usually based on
  781.            return values from parse_applications_command().
  782.         '''
  783.         res = ""
  784.         if action == "default-allow":
  785.             res = self.set_default_application_policy("allow")
  786.         elif action == "default-deny":
  787.             res = self.set_default_application_policy("deny")
  788.         elif action == "default-skip":
  789.             res = self.set_default_application_policy("skip")
  790.         elif action == "list":
  791.             res = self.get_application_list()
  792.         elif action == "info":
  793.             res = self.get_application_info(profile)
  794.         elif action == "update" or action == "update-with-new":
  795.             res = self.application_update(profile)
  796.             if action == "update-with-new":
  797.                 res += "\n" + self.application_add(profile)
  798.         else:
  799.             err_msg = _("Unsupported action '%s'") % (action)
  800.             raise UFWError(err_msg)
  801.  
  802.         return res
  803.  
  804.     def continue_under_ssh(self):
  805.         '''If running under ssh, prompt the user for confirmation'''
  806.         proceed = True
  807.         if self.backend.do_checks and ufw.util.under_ssh():
  808.             prompt = _("Command may disrupt existing ssh connections.")
  809.             prompt += _(" Proceed with operation (y|n)? ")
  810.             os.write(sys.stdout.fileno(), prompt)
  811.             ans = sys.stdin.readline().lower().strip()
  812.             if ans != "y" and ans != "yes":
  813.                 proceed = False
  814.  
  815.         return proceed
  816.  
  817.